package com.example.codegen;

import com.example.utils.CJUtils;
import com.example.utils.classNode;

import java.util.ArrayList;
import java.util.Optional;

public class Variables implements Expression {
    // resvered for force cast
    // please wrap the type and variables in a bracket if you want to force cast like (int)(a.b)
    public Optional<Type> type;
    public ArrayList<Variable> variables;

    public Variables(Optional<Type> type, ArrayList<Variable> variables) {
        this.type = type;
        this.variables = variables;
    }

    public String toJava() {
        return (type.isPresent() ? "(" + type.get().toJava() + ")" : "") + (type.isPresent() ? "(" : "") + String.join(".", variables.stream().map(Expression::toJava).toArray(String[]::new)) + (type.isPresent() ? ")" : "");
    }

    public String toGo() {
        var leading = variables.get(0);
        // for golang, we use a new STATICX struct to store all static fields, and check it by whether it contains upper case
        // if it contains upper case, we use STATICX, otherwise, we use x
        // static fields are promised to be accessed by "struct.field"
        var leading_name = leading.containUpper() ? "STATIC" + leading.toGo().toUpperCase() : "" + leading.toGo();
//        var leading_name = leading.toGo();
        // for a.b.c => a.B.C since all fields we defined are public(use upper case)
        var rest = variables.subList(1, variables.size());
        String variables = String.join(".", rest.stream().map(variable -> {
            variable.identifier = variable.identifier.toUpperCase();
            return variable;
        }).map(Expression::toGo).toArray(String[]::new));
        if (!variables.isEmpty()) {
            return leading_name + "." + variables;
        }
        return leading_name;
    }

    @Override
    public String toJs() {
        return String.join(".", variables.stream().map(Expression::toJs).toArray(String[]::new));
    }

    @Override
    public String toCSharp() {
        return (type.isPresent() ? "(" + type.get().toCSharp() + ")" : "") + (type.isPresent() ? "(" : "") + String.join(".", variables.stream().map(Expression::toCSharp).toArray(String[]::new)) + (type.isPresent() ? ")" : "");
    }

    public static String getFromWhichClass(String classNow, String field){
        String classNameTrue = classNow;
        if(classNameTrue.startsWith("Array<")){
            int lastLeftIndex = classNameTrue.lastIndexOf('<');
            int firstRightIndex = classNameTrue.indexOf('>');
            classNameTrue = classNameTrue.substring(lastLeftIndex+1,firstRightIndex);
            if(classNameTrue.charAt(0) == '?')
                classNameTrue = classNameTrue.substring(1);
        }
        classNode tmp = CJUtils.classNodes.get(classNameTrue.toLowerCase());
        if(tmp != null){
            for(Field field1 : tmp.fields){
                if(field1.fieldName.toCJ().replaceFirst("HadTurn$","").equals(field)){
                    return classNameTrue;
                }
            }
            // no found
            if(tmp.parent != null){
                return getFromWhichClass(tmp.parent, field);
            }
        }
        return "";
    }
    public static String getClassNameOfField(String fieldName, String className){
        String classNameTrue = className;
        if(classNameTrue.startsWith("Array<")){
            int lastLeftIndex = classNameTrue.lastIndexOf('<');
            int firstRightIndex = classNameTrue.indexOf('>');
            classNameTrue = classNameTrue.substring(lastLeftIndex+1,firstRightIndex);
            if(classNameTrue.charAt(0) == '?')
                classNameTrue = classNameTrue.substring(1);
        }
        classNode nowNode = CJUtils.classNodes.get(classNameTrue.toLowerCase());
        if(nowNode == null) return "";
        for(Field field: nowNode.fields){
            if(field.fieldName.toCJ().equals(fieldName)){
                return field.fieldType.toCJ().replace("?", "");
            }
        }
        return "";
    }
    @Override
    public String toCJ() {
        String res = "";
        if(CJUtils.isVariableNameNormalized) {
            int size = variables.size();
            if (size < 1)
                return "";
            else if (size == 1)
                res = variables.get(0).toCJ();
            else {
                String firstVar = variables.get(0).toCJ();
                String classOfFirst = firstVar.replaceFirst("(_\\d+)$", "");

                if (CJUtils.classNodes.containsKey(classOfFirst))
                    classOfFirst = CJUtils.classNodes.get(classOfFirst).className;
                else {
                    String[] arrayClass = classOfFirst.split("\\.");
                    if(arrayClass.length > 1){
                        String className = arrayClass[0].replaceFirst("(_array_\\d+_\\d+)$", "");
                        if (CJUtils.classNodes.containsKey(className))
                            classOfFirst = CJUtils.classNodes.get(className).className;
                    }

                }

                for (int i = 1; i < variables.size(); i++) {
                    String tmp = variables.get(i).toCJ();
                    String tmpClass = getFromWhichClass(classOfFirst, variables.get(i).identifier);
                    String frontClass = tmpClass.equals("") ? "" : tmpClass + "_";
                    if (!firstVar.equals(classOfFirst))
                        res += firstVar + ".getOrThrow()." + frontClass;
                    else
                        res += firstVar + "." + frontClass;
                    firstVar = tmp;
                    classOfFirst = getClassNameOfField(variables.get(i).identifier, tmpClass);
                }
                res += firstVar;
            }
        } else {
            for (int i = 0; i < variables.size()-1; i++) {
                String tmp = variables.get(i).toCJ();
                if(Type.classSet.contains(tmp)){
                    res += tmp + ".";
                } else {
                    res += tmp + ".getOrThrow().";
                }
            }
            res += variables.get(variables.size()-1).toCJ();
        }
        if(type.isPresent()){
            String typeName = type.get().toCJ();
//            if(typeName.startsWith("?Array<"))
//                return res;
            if(typeName.charAt(0) == '?')
                return "turnClass(" + res + ")";
        }
        return res;
    }
}
